package com.wanban.downloader.db.v3

import android.content.ContentValues
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.text.TextUtils
import androidx.core.database.getFloatOrNull
import androidx.core.database.getIntOrNull
import androidx.core.database.getLongOrNull
import androidx.core.database.getStringOrNull
import com.wanban.downloader.db.DownloadEntity
import com.wanban.downloader.model.Status
import com.wanban.downloader.utils.CacheM3U8Utils
import com.wanban.downloader.utils.CommonUtils
import java.util.concurrent.atomic.AtomicInteger

class DatabaseManager {

    private val mOpenCounter = AtomicInteger()
    private var mDatabase: SQLiteDatabase? = null
    private val column = arrayOf(
        DownloadSQLiteHelper.Columns.URL,
        DownloadSQLiteHelper.Columns.VIDEO_NAME,
        DownloadSQLiteHelper.Columns.VIDEO_ID,
        DownloadSQLiteHelper.Columns.COVER_URL,
        DownloadSQLiteHelper.Columns.EPISODE,
        DownloadSQLiteHelper.Columns.EPISODE_NAME,
        DownloadSQLiteHelper.Columns.SOURCE_ID,
        DownloadSQLiteHelper.Columns.CATEGORY,
        DownloadSQLiteHelper.Columns.PERCENT,
        DownloadSQLiteHelper.Columns.CONTENT_LENGTH,
        DownloadSQLiteHelper.Columns.STATE,
        DownloadSQLiteHelper.Columns.WATCH_TIME,
        DownloadSQLiteHelper.Columns.CREATE_TIME,
        DownloadSQLiteHelper.Columns.FILE_PATH,
        DownloadSQLiteHelper.Columns.RETRY_TIME,
        DownloadSQLiteHelper.Columns.FILE_DIR_PATH,
        DownloadSQLiteHelper.Columns.DOWNLOAD_TYPE
    )

    //写
    @Synchronized
    fun openWritableDatabase(): SQLiteDatabase? {
        if (mOpenCounter.incrementAndGet() == 1) {
            mDatabase = mDatabaseHelper?.writableDatabase
        }
        return mDatabase
    }

    @get:Synchronized
    val readableDatabase: SQLiteDatabase?
        //读
        get() {
            if (mOpenCounter.incrementAndGet() == 1) {
                // Opening new database
                mDatabase = mDatabaseHelper?.readableDatabase
            }
            return mDatabase
        }

    //同步关闭
    @Synchronized
    fun close() {
        if (mOpenCounter.decrementAndGet() == 0) {
            mDatabase?.close()
        }
    }


    @Synchronized
    fun getTask(selection: String, selectionArgs: Array<String>): DownloadEntity? {
        val db = readableDatabase?:return null
        var cursor: Cursor? = null
        try {
            db.beginTransaction()
            cursor = db.query(
                DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO,
                column, selection, selectionArgs,
                null, null, null, null
            ) ?: return null
            db.setTransactionSuccessful()
            if (cursor.moveToFirst()) {
                return getDataByCursor(cursor, false)
            }
            return null
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        } finally {
            cursor?.close()
            db.endTransaction()
            close()
        }
    }

    @Synchronized
    fun getTasks(selection: String?, selectionArgs: Array<String>?): ArrayList<DownloadEntity>? {
        val db = readableDatabase?:return null
        var cursor: Cursor? = null
        try {
            db.beginTransaction()

            cursor = db.query(
                DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO,
                column, selection, selectionArgs,
                null, null, null, null) ?: return null
            val items = ArrayList<DownloadEntity>()
            db.setTransactionSuccessful()
            while (cursor.moveToNext()) {
                val item = getDataByCursor(cursor, false)?:continue
                items.add(item)
            }
            return items
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        } finally {
            cursor?.close()
            db.endTransaction()
            close()
        }
    }

    @Synchronized
    fun getAllTasksWithM3U8(): ArrayList<DownloadEntity>?{
        val db = readableDatabase?:return null
        var cursor: Cursor? = null
        try {
            db.beginTransaction()
//            val selection = DownloadSQLiteHelper.Columns.STATE + "!=? AND " + DownloadSQLiteHelper.Columns.STATE + "!=?"
//            val selectionArgs = arrayOf(Status.SUCCESS.toString(), Status.DELETE.toString())
            cursor = db.query(
                DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO,
                null, null, null,
                null, null, null, null) ?: return null
            val items = ArrayList<DownloadEntity>()
            db.setTransactionSuccessful()
            while (cursor.moveToNext()) {
                val item = getDataByCursor(cursor, isContainerM3U8 = true)?:continue
                items.add(item)
            }
            return items
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        } finally {
            cursor?.close()
            db.endTransaction()
            close()
        }
    }


    private fun query(db: SQLiteDatabase, url: String): String? {
        var cursor: Cursor? = null
        return try {
            db.beginTransaction()
            cursor = db.query(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO,
                arrayOf(DownloadSQLiteHelper.Columns.URL),
                DownloadSQLiteHelper.Columns.URL + " = ?",
                arrayOf(url), null, null, null)
            db.setTransactionSuccessful()
            if (cursor?.moveToNext() == true) {
                cursor.getStringOrNull(cursor.getColumnIndexOrThrow(DownloadSQLiteHelper.Columns.URL))
            } else {
                null
            }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        } finally {
            db.endTransaction()
            cursor?.close()
        }
    }

    @Synchronized
    fun insert(task: DownloadEntity): Long{
        var index = -1L
        val db = openWritableDatabase() ?: return index
        return try {
            val taskUrl = query(db, task.url)
            db.beginTransaction()
            val values = getContentValues(task)
            index = if (TextUtils.isEmpty(taskUrl)) { // 表中无此数据，需要插入
                db.insert(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO, null, values)
            } else { // 已经在表中，无需再次插入，需要更新
                db.update(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO, values,
                    DownloadSQLiteHelper.Columns.URL+"=?", arrayOf(task.url)).toLong()
            }
            db.setTransactionSuccessful()
            index
        } catch (e: Exception) {
            e.printStackTrace()
            index
        } finally {
            db.endTransaction()
            close()
        }
    }

    fun delete(url: String): Long{
        val db = openWritableDatabase() ?: return -1L
        return try {
            db.beginTransaction()
            val index = db.delete(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO, DownloadSQLiteHelper.Columns.URL +"=?", arrayOf(url)).toLong()
            db.setTransactionSuccessful()
            index
        } catch (e: Exception) {
            e.printStackTrace()
            -1L
        } finally {
            db.endTransaction()
            close()
        }
    }

    @Synchronized
    fun deleteAll(){
        val db = openWritableDatabase() ?: return
        try {
            db.beginTransaction()
            db.delete(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO, null, null).toLong()
            db.setTransactionSuccessful()
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            db.endTransaction()
            close()
        }
    }

    private fun getDataByCursor(cursor: Cursor, isContainerM3U8: Boolean): DownloadEntity? {
        val item = DownloadEntity()
        val url = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.URL))
        if (TextUtils.isEmpty(url)) return null
        item.url = url!!
        val videoName = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.VIDEO_NAME))
        if (TextUtils.isEmpty(videoName)) return null
        item.videoName = videoName!!
        item.coverUrl = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.COVER_URL))
        item.videoId = cursor.getLongOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.VIDEO_ID))?:-1L
        item.watchTime = cursor.getLongOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.WATCH_TIME))?:0
        item.contentLength = cursor.getLongOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.CONTENT_LENGTH))?:0L
        item.state = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.STATE))?: Status.IDLE
        item.episode = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.EPISODE))?:0
        item.category = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.CATEGORY))?:0
        item.percent = cursor.getFloatOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.PERCENT))?:0f
        item.sourceId = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.SOURCE_ID))?:0
        item.retryTime = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.RETRY_TIME))?:0
        item.episodeName = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.EPISODE_NAME))
        item.filePath = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.FILE_PATH))
        item.fileDirPath = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.FILE_DIR_PATH))
        item.createTime = cursor.getLongOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.CREATE_TIME))?:0L
        item.downloadType = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.DOWNLOAD_TYPE))?:0

        // header需要转换
        val headerStr = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.HEADER))
        if (!TextUtils.isEmpty(headerStr)) {
            item.header = CommonUtils.string2Map(headerStr)
        }

        if (isContainerM3U8) { // 包含m3u8
            item.m3u8 = CacheM3U8Utils.getM3U8FromCache(item.getFileName())
        }
        // 从指定的
//        val m3u8Json = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.M3U8))
//        if (!TextUtils.isEmpty(m3u8Json)) {
//            item.m3u8 = Gson().fromJson(m3u8Json, M3U8::class.java)
//        }
        return item
    }


    private fun getContentValues(item: DownloadEntity): ContentValues {
        val values = ContentValues()
        values.put(DownloadSQLiteHelper.Columns.URL, item.url)
        values.put(DownloadSQLiteHelper.Columns.VIDEO_NAME, item.videoName)
        values.put(DownloadSQLiteHelper.Columns.COVER_URL, item.coverUrl)
        values.put(DownloadSQLiteHelper.Columns.VIDEO_ID, item.videoId)
        values.put(DownloadSQLiteHelper.Columns.WATCH_TIME, item.watchTime)
        values.put(DownloadSQLiteHelper.Columns.CONTENT_LENGTH, item.contentLength)
        values.put(DownloadSQLiteHelper.Columns.STATE, item.state)
        values.put(DownloadSQLiteHelper.Columns.EPISODE, item.episode)
        values.put(DownloadSQLiteHelper.Columns.CATEGORY, item.category)
        values.put(DownloadSQLiteHelper.Columns.PERCENT, item.percent)
        values.put(DownloadSQLiteHelper.Columns.SOURCE_ID, item.sourceId)
        values.put(DownloadSQLiteHelper.Columns.RETRY_TIME, item.retryTime)
        values.put(DownloadSQLiteHelper.Columns.EPISODE_NAME, item.episodeName)
        values.put(DownloadSQLiteHelper.Columns.FILE_PATH, item.filePath)
        values.put(DownloadSQLiteHelper.Columns.FILE_DIR_PATH, item.fileDirPath)
        values.put(DownloadSQLiteHelper.Columns.CREATE_TIME, item.createTime)
        values.put(DownloadSQLiteHelper.Columns.DOWNLOAD_TYPE, item.downloadType)
//        if (item.m3u8 != null) {
//            // 本地化
//            val m3u8Json = Gson().toJson(item.m3u8)
//            values.put(DownloadSQLiteHelper.Columns.M3U8, m3u8Json)
//        }
        if (item.header != null) {
            values.put(DownloadSQLiteHelper.Columns.HEADER, CommonUtils.map2String(item.header))
        }
        return values
    }


    //强制真正的关闭
    @Synchronized
    fun trueClose() {
        mDatabase?.close()
    }

    companion object {
        private var instance: DatabaseManager? = null
        private var mDatabaseHelper: DownloadSQLiteHelper? = null

        //初始化
        @Synchronized
        fun initializeInstance(helper: DownloadSQLiteHelper?) {
            if (instance == null) {
                synchronized(DatabaseManager::class.java) {
                    if (null == instance) {
                        instance = DatabaseManager()
                        mDatabaseHelper = helper
                    }
                }
            }
        }

        @Synchronized
        fun getInstance(): DatabaseManager? {
            checkNotNull(instance) {
                DatabaseManager::class.java.simpleName + " is not initialized, call initializeInstance(..) method first."
            }
            return instance
        }
    }
}